Como la implementaci\'on en ASM de este filtro result\'o con una performance peor que la version de C optimizada, optamos por rehacer la experimentaci\'on en busca de 
ideas para nuevas optimizaciones. Sin embargo, en medio de la nueva experimentaci\'on, nos encontramos con un comportamiento inesperado del procesador. 
Dicha situaci\'on est\'a detallada m\'as adelante, en el experimento que le dio origen, junto con la investigaci\'on que realizamos en un intento de comprender 
este fen\'omeno.

\subsection{Descripci\'on}

La idea de este filtro es tomar cada pixel y aplicarle la siguente f\'ormula:

\begin{equation*}
dst(i,j) = src(i,j)*coef(i,j)
\end{equation*}
donde: 
\begin{equation*}
coef(i,j) =  \frac{1}{255,0}(\lfloor \frac{i}{cant_{filas}}*255,0 \rfloor \oplus \lfloor \frac{j}{cant_{cols}}*255,0 \rfloor)
\end{equation*}

Como se ve arriba, la f\'ormula contiene como varible la posici\'on del pixel. Ademas, tenemos el hecho de que $coef(i,j)$ debe representarse en punto flotante,
lo que nos fuerza a llevar los valores $r$,$g$ y $b$ a representaci\'{o}n de punto flotante, para luego poder multiplicar los canales rojo, verde y azul por dicho 
coeficiente. Estas dos situaciones hacen que s\'{o}lo sea posible procesar en paralelo un p\'{i}xel, pues el ancho
de un registro multimedia es menor al tama\~{n}o que ocupar\'{i}an dos p\'{i}xeles con la precisi\'{o}n extendida a punto flotante de precisi\'{o}n simple, incluso
ignorando el canal alfa. Es por este motivo que no podemos aunmentar el procesamiento a m\'as de un pixel en paralelo, y por lo tanto no podemos mejorar la performance 
en este aspecto. 

La idea de nuestro algoritmo es la siguiente.

\begin{codesnippet}
\begin{verbatim}
        sea s un puntero al principio de la zona indicada en la imagen fuente
        sea d un puntero al principio de la ultima fila de la imagen destino
        miestras s permanezca en la imagen
                leer 4 pixeles a partir de s, pi pi+1 pi+2 pi+3
                para todo pixel pi+l, con 0 <= l < 4 , pi+l en la pocision (j,k)
                        obtener coef(j,k)
                        sea ri+l = pi+l * coef(j,k)
               grabar 4 pixeles a partir de d, ri ri+1 ri+2 ri+3
               d = d + 16
               s = s + 16
\end{verbatim}
\end{codesnippet}

Para procesar cada p\'{i}xel, debemos obtener, en primer lugar, $coef(i,j)$. Para calcularlo, debemos utilizar continuamente las instrucciones de 
conversi\'{o}n de datos, para pasar de entero a punto flotante y viceversa. En primer lugar, debemos obtener el valor $\frac{i}{cant_{filas}}$, para lo cual
debemos convertir tanto el $i$ como el $cant_{filas}$ a punto flotante para poder hacer la divisi\'{o}n. Luego, multiplicamos dicho resultado por la constante
$255.0$, almacenada en punto flotante. An\'{a}logamente, hacemos las operaciones necesarias sobre $j$ y $cant_{cols}$, y pasamos estos dos resultados
a representaci\'{o}n entera, para hacer el \verb|xor| entre ellos. Una vez hecha la operaci\'{o}n l\'{o}gica, volvemos a castear a punto flotante y 
dividimos por la constante 
$255.0$, obteniendo finalmente $coef(i,j)$. As\'{i}, contamos con un registro multimedia de esta forma:

\begin{center}
   \begin{tabular}{| l | c | r | c | r | c | r | c | r |c | r |c | r |}
     \hline
     $coef(i,j)$& $*$ & $*$ & $*$ \\ \hline
   \end{tabular}
\end{center}

El siguiente paso es extender (dos veces) la precisi\'{o}n de los tres canales de p\'{i}xel fuente (es decir, desde byte hasta double word) y luego castear
dichos valores a float, para poder hacer el producto con $coef(i,j)$. Pero, para poder multiplicar todos los canales en paralelo, es necesario que $coef(i,j)$
est\'{e} ``repetido`` en el registro; esto es, deseamos que haya un registro con esta informaci\'{o}n guardada:

\begin{center}
   \begin{tabular}{| l | c | r | c | r | c | r | c | r |c | r |c | r |}
     \hline
     $coef(i,j)$& $coef(i,j)$ & $coef(i,j)$ & $0$ \\ \hline
   \end{tabular}
\end{center}

Esto podemos lograrlo f\'{a}cilmente, vali\'{e}ndonos de las operaciones de shift. En primer lugar, desplazamos 12 bytes a izquierda el registro con el 
$coef(i,j)$ en el primer float, quedando el registro de esta manera:

\begin{center}
   \begin{tabular}{| l | c | r | c | r | c | r | c | r |c | r |c | r |}
     \hline
     $0$& $0$ & $0$ & $coef(i,j)$ \\ \hline
   \end{tabular}
\end{center}

Volviendo a shiftear 12 bytes, esta vez hacia la derecha, el registro queda de esta manera:

\begin{center}
   \begin{tabular}{| l | c | r | c | r | c | r | c | r |c | r |c | r |}
     \hline
     $coef(i,j)$& $0$ & $0$ & $0$ \\ \hline
   \end{tabular}
\end{center}

Luego, forzando ceros en otro registro (que tambi\'{e}n puede hacerse con la operaci\'{o}n \verb|pxor|), copiando el $coef(i,j)$ en el registro con ceros y shifteando
4 bytes a izquierda, obtenemos un segundo registro as\'{i}:

\begin{center}
   \begin{tabular}{| l | c | r | c | r | c | r | c | r |c | r |c | r |}
     \hline
     $0$& $coef(i,j)$ & $0$ & $0$ \\ \hline
   \end{tabular}
\end{center}

y un \verb|por| entre el registro original y este obtenemos el $coef(i,j)$ en los dos primeros floats. Operando de forma an\'{a}loga se puede obtener el $coef(i,j)$
en el tercer float. Notese que esto se podria optimizar utilizando la instrucci\'on \verb|shufps| con una mascara apropiada, sin embargo la version original se 
realizo con shifteos y mantenemos esta version en este filtro.

Una vez hecho esto, extendemos la precisi\'{o}n de cada canal del p\'{i}xel fuente, desempaquetando con ceros, hasta llegar a double word para
luego casteamar a float. Con lo hecho anteriormente tenemos, por un lado, el registro con el p\'{i}xel de entrada as\'{i}:

\begin{center}
   \begin{tabular}{| l | c | r | c | r | c | r | c | r |c | r |c | r |}
     \hline
     $float(b_0)$&$float(g_0)$ & $float(r_0)$ & $0$ \\ \hline
   \end{tabular}
\end{center}

y por otro lado:

\begin{center}
   \begin{tabular}{| l | c | r | c | r | c | r | c | r |c | r |c | r |}
     \hline
     $coef(i,j)$& $coef(i,j)$ & $coef(i,j)$ & $0$ \\ \hline
   \end{tabular}
\end{center}

con lo cual, lo que debemos hacer es el producto (en paralelo) de estos dos registros. Una vez hecho esto, s\'{o}lo resta volver a convertir los datos 
a representaci\'{o}n entera y empaquetarlos dos veces, con truncamiento, para volverlos a su tama\~{n}o original. En este punto vale la pena aclarar que,
si bien nuestro programa procesa un p\'{i}xel en paralelo, hemos decidido aprovechar la lectura de 16 bytes a la imagen fuente y procesar los 4 p\'{i}xeles
leidos dentro de la misma iteraci\'{o}n, con lo cual nuestro programa debe acomodar los p\'{i}xeles dentro de un registro (que contendr\'{a} la data que 
finalmente se mover\'{a} a la imagen destino) a medida que los procesa. Sin embargo, hemos decidido no incluir esto en esta parte descripci\'{o}n pues ya 
esta expuesto en el pseudoc\'odigo, y no aporta a la comprensi\'{o}n de c\'{o}mo nuestro programa procesa cada p\'{i}xel.

\subsection{Experimentaci\'on}

\subsubsection{Secuencial vs. Vectorial}

En este experimento seguimos con la idea del experimento 2.2.4, es decir, comprobar la utilidad de programar en un lenguaje de bajo nivel, siempre de manera emp\'irica.
Creemos que filtro presenta una implementaci\'on antag\'onica a la del filtro cropflip, pues al trabajar con un solo pixel, cobra m\'as importancia el 
procesamiento del dato que la transferencia del mismo. Tomando esto como axioma, mediante este experimento buscamos conocer la utilidad de usar lenguaje de bajo nivel
para optimizar la transformaci\'on de la informaci\'on, notando que con esto nos referimos a la realizaci\'on 
de calculos aritmeticos simples.

\begin{figure}[H]
    \includegraphics[width=0.8\textwidth]{sierpinski14}
  \label{sierpinski14}
\end{figure}

Los resultados expuestos en el gr\'afico muestran que el compilador usado, gcc, realiza una mejor optimizaci\'on que la que conseguida implementando en bajo nivel. 
Asi concluimos, partiendo del axioma previamente mencionado, que la transformaci\'on de la informaci\'on  es un proceso que se puede optimizar de manera 
autom\'atica. Esta conclusi\'on apoya el hecho de que el compilador lograra una mejor performance optimizando el c\'odigo C.


\subsubsection{CPU vs. Bus de memoria}

La idea de este experimento es igual a la del experimento 2.2.5. Es decir, intentar determinar, mediante el agregado de instrucciones aritm\'eticas y accesos a memoria,
el factor limitante para la performance del filtro.

Sin embargo, durante la experimentaci\'on, nos encontramos con un resultado inesperado. Al agregar $k$ instrucciones aritm\'eticas seguidas,\emph{ add rax, rbx},
sabemos que esta secuencia de instrucciones no es paralelizable, pues al usar \emph{rax} como acumulador se crea dependencia con la ejecuci\'on de la instrucci\'on
previa.

El problema fue que cuando agregamos 4 instrucciones el filtro tard\'o una mayor cantidad de ciclos de clock en aplicarse que al agregarle 8, 16 e incluso 32 instrucciones. Sin embargo,
la medici\'on obtenida al agregar 8 s\'i era menor que la obtenida con 16, que a su vez era menor que la de 32. Las diferencias se encontraban en el orden del 0.001\%.

Luego de realizar multiples pruebas obteniendo el mismo resultado, decidimos poner las instrucciones separadas, por lineas pertenecientes al filtro, en grupos de a 4.
Entonces comprobamos que efectivamente las 8 instrucciones separadas en grupos de a 4, tardaban m\'as que las 4 originales. Esto nos llev\'o a creer 
que cuando el microprocesador detecta que tiene que resolver una gran cantidad de instrucciones aritm\'eticas, activa de alg\'un modo una modalidad donde puede 
resolverlas de manera m\'as eficiente, probablemente con el coste de perder alguna funcionalidad.

Teniendo esta hip\'otesis en mente, buscamos alg\'un modo de confirmarla. Nuestro recorrido por la web en busca de respuestas resulto infructuoso. No encontramos ning\'un
sitio, entendible para nuestras capacidades de humildes estudiantes, %creo firmemente que ''mortales'' quedaba mejor
que brindara la informaci\'on requerida para corroborar nuestra hip\'otesis. Agotados todos nuestros recursos, recurrimos a un ser m\'as sabio en busca de respuestas.
Este ente iluminado, perteciente a la mistica y sabia raza JTP, nos aconsejo fijar el clock de la m\'aquina y luego repetir los experimentos. 
Como el lector se imaginar\'a, esta simple idea nos llen\'o de esperanza, pues el hecho de que el procesador aumentara su ritmo de trabajo si se le demandaba 
m\'as, era una explicaci\'on razonable. Una explicaci\'on con un minimo margen de racionalidad nos habr\'ia devuelto las esperanzas. Nos permitiria creer que, 
aunque no pudiesemos entenderla o hallarla, detras de todas estas restricciones raras y requisistos, en apariencia, in\'utiles de Intel$^{\textcopyright}$ se 
hallaba una estructuraci\'on 
que tenia l\'ogica. Era una explicaci\'on perfectamente v\'alida, pero incorrecta.

A\'un fijando el clock del cpu, el resultado era el mismo. Con 4 instrucciones aritmeticas tardaba m\'as que con 8, 16 o 32. Sin la posibilidad de confirmar nuestra 
hip\'otesis por falta de informaci\'on, y con la hip\'otesis del ser JTPsiano refutada, nos vimos en la obligaci\'on de declarar que 4 instrucciones era un agregado 
demasiado peque\~no para permitirnos observar un comportamiento general. Asi mismo, una diferencia del 0.001\% en magnitudes del orden de los millones
era despreciable. 

Es por este motivo que las mediciones de este grafico se realizaron agregando 16, 32, 64 y 128 instrucciones de cada tipo. Las mediciones resultantes tienen 
la coherencia esperada. Sin embargo, nuestro grupo aun contin\'ua en la busqueda de respuestas a estos y otros sucesos paranormales, en un intento por comprender
lo que simplemente denominaremos como \emph{la magia negra de} Intel$^{\textcopyright}$.

\begin{figure}[H]
    \includegraphics[width=1\textwidth]{sierpinski22}
  \label{sierpinski14}
\end{figure}
 
Estos son los valores representados en el gr\'afico anterior:
 
\begin{center}
   \begin{tabular}{| c | c | c | c | r | c | r | c | r |c | r |c | r |}
     \hline
     Versi\'on del filtro en assembler & Media & Varianza  \\ \hline
     Original& 16003204 & 5326.30 \\ \hline
     Con 16 aritm\'eticas extra & 16461742.04 & 4888.55 \\ \hline
     Con 32 aritm\'eticas extra & 16719922.70 & 6001.46 \\ \hline
     Con 64 aritm\'eticas extra & 17273397.91 & 15220.54 \\ \hline
     Con 128 aritm\'eticas extra & 27753078.54 & 17587.91 \\ \hline
     Con 16 accesos a memoria extra & 16416527.67 & 4426.29 \\ \hline
     Con 32 accesos a memoria extra & 16428864.95 & 4511.47 \\ \hline
     Con 64 accesos a memoria extra & 17774791.25 & 14042.75 \\ \hline
     Con 128 accesos a memoria extra & 32238085.87 & 13999.75 \\ \hline
     \end{tabular}
\end{center}

Los datos reflejados en este gr\'afico apuntan en la misma direcci\'on que los obtenidos en el experimento 2.2.5, lo que refuerza nuestra teoria de que un 
acceso a memoria es mas lento que la ejecuci\'on de una instruccion aritm\'etica en general. Al no presentar una variaci\'on en la relaci\'on entre los valores, estamos 
obligados a llegar a la misma conclusi\'on que en dicho caso.

